/*****************************************************************************
Copyright 2006 Kervin L. Pierre
    EMAIL: otlkcon@adevsoft.com

This file is part of Otlkcon.

Otlkcon is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Otlkcon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Lesser GNU General Public License for more details.

You should have received a copy of the Lesser GNU General Public License
along with Otlkcon; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*******************************************************************************/

#include "StdAfx.h"
#include "otlkcon_generic_msgstore.h"
#include "otlkcon_account.h"
#include "mstore.h"

extern map< O_ENTRYID *, void *, otlkcon_entryid_less > otlkcon_event_objects;
extern CRITICAL_SECTION    otlkcon_objects_cs;

otlkcon_generic_msgstore::otlkcon_generic_msgstore(void)
{
    className = _strdup("otlkcon_generic_msgstore");
    memObj = NULL;
    objCount = 0;
    isSpoolerStore = 0;
	m_lpParentProvider = NULL;
	m_ulCachedStoreLogOffFlags = LOGOFF_ABORT;
    prop2str = NULL;
    ulUIParam = 0;
    logonFlags = 0;
}

otlkcon_generic_msgstore::~otlkcon_generic_msgstore(void)
{
    ;
}

STDMETHODIMP_(ULONG) otlkcon_generic_msgstore::AddRef(void)
{
	ULONG ulRef = InterlockedIncrement((LONG*)&cRef);
	//cRef++;
	OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"%S->%S::AddRef %d\n",
		objName, className, ulRef);
	
	return ulRef;
}

STDMETHODIMP_(ULONG) otlkcon_generic_msgstore::Release(void)
{
	ULONG ulRef = InterlockedDecrement((LONG*)&cRef);
	//cRef++;
	OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"%S->%S::Release %d\n",
		objName, className, ulRef);

	if(0 == ulRef)
	{
		Destroy();
	} // if

	return ulRef;
}

HRESULT otlkcon_generic_msgstore::Create(char * df, MAPIUID ig, otlkcon_memory *m )
{
    HRESULT result = S_OK;
    SPropValue defaultProps[30] = {0};
	ULONG count=1, propCount = 0;
    O_IFolder *rootFolder;
    O_ENTRYID leid = {0};

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"\n>>>>>>>>>otlkcon_generic_msgstore::Create()<<<<<<<<<<<<<<\n\n");

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Create");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Create");

    memObj = new otlkcon_memory( m );
    databaseFilename = memObj->_strdup( df );
    result = O_IProp::Create(this, ig, otlkcon_imsgstore_guid );
    if( result != S_OK )
    {
        // This object already exists
        goto create_fail;
    }

	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
        OTLKCON_DEBUGBREAK
		result = E_FAIL;
        goto create_fail;
	} // if

	if( result != S_OK )
    {
        memObj->free(databaseFilename);
		databaseFilename = NULL;
		delete memObj;
		memObj = NULL;
		OTLKCON_DEBUGBREAK
        goto create_fail;
    }

    // Certain O_IProp methods refer to the message store object
    msgstore = this;

    // FIXME: I am getting conflicting info on this.  Should this be
    //        unique to the file level or unique to the service provider?
    entryID->storeInstanceGuid = entryID->instanceGuid;

    // MsgStore never does GetProps( PR_ENTRYID, ... ) on itself right?
    // Because this would break this
    computedProperties.insert( PR_ENTRYID );

    // Now lets create the mapping table
    // TODO:  Should this be shared across all message stores?
    int sqlRes;
    int rowCount;

    //Does the name/id mapping table already exist?
    sqlRes = sqlObj->sqlCmdSingleInt( &rowCount, 0,
        "SELECT COUNT(*) FROM sqlite_master WHERE ( type='table' ) AND\
        (name='namedproperty_mapping_table')" );
    if ( rowCount == 0 )
    {
        sqlRes = sqlObj->sqlCommand(
        "CREATE TABLE namedproperty_mapping_table \
          ( PropTag INTEGER PRIMARY KEY, ulKind INTEGER, id INTEGER, name TEXT, \
            property_set TEXT )" );
        if( sqlRes != S_OK )
        {
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
            goto create_fail;
        }

        //sqlRes = sqlObj->sqlCommand(
        //    "CREATE TRIGGER insert_namedproperty_mapping_table_trigger \
	       //     AFTER INSERT ON namedproperty_mapping_table            \
        //        FOR EACH ROW        \
        //        BEGIN               \
        //            SELECT proptag  \
        //            FROM named_property_mapping                 \
        //            WHERE ( property_set = new.property_set )   \
        //                AND ( id = new.id )                     \
		      //          AND ( ulKind = new.ulKind );            \
        //        END" );
        sqlRes = sqlObj->sqlCommand(
            "CREATE TRIGGER insert_namedproperty_mapping_table_trigger \
	            AFTER INSERT ON namedproperty_mapping_table            \
               FOR EACH ROW        \
                BEGIN               \
                     SELECT * from namedproperty_mapping_table;          \
               END" );
        if( sqlRes != S_OK )
        {
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
            goto create_fail;
        }
    }

    sqlRes = sqlObj->sqlCommand(
        "CREATE TABLE IF NOT EXISTS account_appointment_folder_mapping_table \
        ( accountId TEXT, guid TEXT, PRIMARY KEY( accountId ) )" );
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        goto create_fail;
    }

    sqlRes = sqlObj->sqlCommand(
        "CREATE TABLE IF NOT EXISTS name_hash_mapping_table \
        ( name TEXT, hash TEXT, guid TEXT, PRIMARY KEY( guid ) )" );
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        goto create_fail;
    }

    sqlRes = sqlObj->sqlCommand(
        "CREATE TABLE IF NOT EXISTS object_version_table \
        ( id INTEGER PRIMARY KEY, name TEXT, version INTEGER )" );
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        goto create_fail;
    }

    /*char *str1 = (char *)calloc(1, 100);
    EntryID2String(entryID, &str1);
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL,
        L"otlkcon_generic_msgstore::Create() for '%S'\n    ID1=%S\n", objName, str1);
    free(str1);*/

    entryID->typeID = MAPI_STORE;
    entryID->primarySupportedInterface = OTLKCON_IMSGSTORE_CLASS;
    entryID->supportedInterfaces |= OTLKCON_IMSGSTORE_CLASS;

    prop2str = new otlkcon_prop2str(memObj->lpAllocateBuffer, memObj->lpFreeBuffer);

    rootFolder = new O_IFolder();

    //Get the root folder.
    //The root folder has the same instance guid as the message store.
    memset(&leid,0,sizeof(O_ENTRYID));
    leid.instanceGuid   = entryID->instanceGuid;
    leid.typeGuid       = otlkcon_imapifolder_guid;
    leid.typeID         = MAPI_FOLDER;
    leid.primarySupportedInterface = OTLKCON_IMAPIFOLDER_CLASS;

    result = rootFolder->Create( this, entryID->instanceGuid, "Root Folder", NULL );
    if( result == S_OK )
    {
        // Reset the type to FOLDER_ROOT
        defaultProps[propCount].ulPropTag  = PR_FOLDER_TYPE;
        defaultProps[propCount].Value.l    = FOLDER_ROOT;
	    result = rootFolder->_IntSetProps(1,(LPSPropValue)&defaultProps,NULL);
    }
    else if( result == MAPI_E_COLLISION )
    {
        memset(&leid,0,sizeof(O_ENTRYID));
        leid.instanceGuid   = entryID->instanceGuid;
        leid.typeGuid       = otlkcon_imapifolder_guid;
        leid.typeID         = MAPI_FOLDER;
        leid.primarySupportedInterface = OTLKCON_IMAPIFOLDER_CLASS;
        result = rootFolder->Load( this, &leid );
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }
    }
    else
    {
        OTLKCON_DEBUGBREAK;
    }
    
    topLevelFolders["ROOT_FOLDER"] = rootFolder;

	OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"\n>>>>>>>>>Leaving otlkcon_generic_msgstore::Create()<<<<<<<<<<<<<<\n\n");

create_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Create");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Create");
    
    return result;

create_fail:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Create");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Create");

    return result;
}

HRESULT otlkcon_generic_msgstore::Create(char * df, MAPIUID ig, MAPIUID tg, otlkcon_memory *m )
{
    HRESULT result = S_OK;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                    L"IMsgStore::Create Can't set typeguid on this object\n");
    result = Create(df,ig,m);

    return result;
}

HRESULT otlkcon_generic_msgstore::Load(char *df, O_ENTRYID *eid, otlkcon_memory *m )
{
    HRESULT result;
    O_ENTRYID leid      = {0};

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Load");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Load");

    memObj = new otlkcon_memory( m );
    databaseFilename = memObj->_strdup( df );

    result = O_IProp::Load( this , eid );
    if( result != S_OK )
    {
        //OTLKCON_DEBUGBREAK;
		memObj->free(databaseFilename);
		databaseFilename = NULL;
		delete memObj;
		memObj = NULL;
		goto load_fail;
    }

    // Check that the object is not already in memory
    leid.instanceGuid   = entryID->instanceGuid;
    leid.typeGuid       = otlkcon_imapifolder_guid;
    leid.typeID         = MAPI_FOLDER;
    leid.primarySupportedInterface = OTLKCON_IMAPIFOLDER_CLASS;
    
    O_IFolder *rootFolder = new O_IFolder();
    result = rootFolder->Load( this, &leid );
    if( result == MAPI_E_NOT_FOUND )
    {
        // The root folder was not found
        // TODO: Do we need a repair mode or utility?
        result = MAPI_E_CORRUPT_DATA;
        goto load_fail;
    }

    if( result != S_OK )
    {
        goto load_fail;
    }
    topLevelFolders["ROOT_FOLDER"] = rootFolder;

load_fail:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Load");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Load");
    
    return result;
}

HRESULT otlkcon_generic_msgstore::Destroy()
{
	HRESULT result = S_OK;

    return result;
}


STDMETHODIMP otlkcon_generic_msgstore::InternalAdvise(
	ULONG						cbEntryID,
	LPENTRYID					lpEntryID,
	ULONG						ulEventMask,
	LPMAPIADVISESINK			lpAdviseSink,
	ULONG						ulFlags, // provider internally uses NOTIFY_SYNC
	ULONG						*lpulConnection)
{
	HRESULT result = S_OK;
	LPNOTIFKEY lpKey;
	LPENTRYID tmpEid;
	ULONG cbTmpEid;

	lastCalledFunctionID = OTLKCON_IMSGSTORE_ADVISE;
	strcpy(lastCalledFunctionName, OTLKCON_IMSGSTORE_ADVISE_STR);
	result = SetLastError(S_OK, "");

	OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
		L"inside IMsgStore->Advise.\n");

	if( lpEntryID == NULL )
	{
		tmpEid   = (LPENTRYID)entryID;
		cbTmpEid = sizeof( O_ENTRYID );
	}
	else
	{
		tmpEid = lpEntryID;
		cbTmpEid = cbEntryID;
	}

	lpKey = (LPNOTIFKEY)memObj->malloc( sizeof(NOTIFKEY) + cbTmpEid );
	lpKey->cb = cbTmpEid;
	memcpy(&(lpKey->ab), tmpEid, cbTmpEid );

	result = lpMAPISup->Subscribe(
		lpKey,
		ulEventMask, 
		ulFlags, lpAdviseSink, lpulConnection);

	return result;
}


STDMETHODIMP otlkcon_generic_msgstore::Advise(
  ULONG cbEntryID,
  LPENTRYID lpEntryID,
  ULONG ulEventMask,
  LPMAPIADVISESINK lpAdviseSink,
  ULONG *lpulConnection       
)
{
	Validate_IMsgStore_Advise(this,cbEntryID,lpEntryID,ulEventMask,
		lpAdviseSink,lpulConnection);

	return InternalAdvise(
		cbEntryID,
		lpEntryID,
		ulEventMask,
		lpAdviseSink,
		0,	//for clients we using deferred notification
		lpulConnection);
}

HRESULT otlkcon_generic_msgstore::Notify( O_ENTRYID *key, ULONG cNotification,
  LPNOTIFICATION lpNotifications,
  ULONG *lpulFlags )
{
    HRESULT result = S_OK;
    LPNOTIFKEY lpKey;

    lpKey = (LPNOTIFKEY)memObj->malloc( sizeof(NOTIFKEY) + sizeof( O_ENTRYID) );
    lpKey->cb = sizeof( O_ENTRYID );
    memcpy(&(lpKey->ab), key, sizeof( O_ENTRYID ) );

    result = lpMAPISup->Notify( lpKey, cNotification,
                                    lpNotifications, lpulFlags );

    return result;
}

STDMETHODIMP otlkcon_generic_msgstore::Unadvise(
  ULONG ulConnection  
)
{
    HRESULT result = S_OK;

    Validate_IMsgStore_Unadvise(this,ulConnection);
    lastCalledFunctionID = OTLKCON_IMSGSTORE_UNADVISE;
    strcpy(lastCalledFunctionName, OTLKCON_IMSGSTORE_UNADVISE_STR);
    result = SetLastError(S_OK, "");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside IMsgStore->Unadvise.\n");

    result = lpMAPISup->Unsubscribe( ulConnection );

    return result;
}

STDMETHODIMP otlkcon_generic_msgstore::CompareEntryIDs(
  ULONG cbEntryID1,
  LPENTRYID lpEntryID1,
  ULONG cbEntryID2,
  LPENTRYID lpEntryID2,
  ULONG ulFlags,
  ULONG *lpulResult  
)
{
    HRESULT result = S_OK;
    O_ENTRYID *id1 = (O_ENTRYID *)lpEntryID1;
    O_ENTRYID *id2 = (O_ENTRYID *)lpEntryID2;

    //Validate_IMsgStore_CompareEntryIDs(this,cbEntryID1,lpEntryID1,
    //            cbEntryID2,lpEntryID2,ulFlags,lpulResult);

    lastCalledFunctionID = OTLKCON_IMSGSTORE_COMPAREENTRYIDS;
    strcpy(lastCalledFunctionName, OTLKCON_IMSGSTORE_COMPAREENTRYIDS_STR);
    SetLastError(S_OK, "");

    //OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
     //   L"inside IMsgStore->CompareEntryIDs.\n");

    //Henry 1.12.2005
	//We need to check if it is ValidEntryID
	//MAPI calls that function with invalid (for our store) entryID

	
	//char *str1 = (char *)calloc(1, 100);
	//if(cbEntryID1 >= sizeof(O_ENTRYID))
	//{
	//	EntryID2String(id1, &str1);
	//} // if
 //   char *str2 = (char *)calloc(1, 100);
	//if(cbEntryID2 >= sizeof(O_ENTRYID))
	//{
	//	EntryID2String(id2, &str2);
	//} // if
 //   OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL|OTLKCON_LOG_FLAG_NO_DATE,
 //       L"    ID1=%S\n    ID2=%S\n", str1, str2);
 //   free(str1);
 //   free(str2);

    *lpulResult = FALSE;

    // IDs too small to compare
    if( cbEntryID1 < sizeof(O_ENTRYID) || cbEntryID2 < sizeof(O_ENTRYID) )
    {
        return MAPI_E_UNKNOWN_ENTRYID;
    }

    if( id1 == NULL || id2 == NULL )
    {
        OTLKCON_DEBUGBREAK
        return MAPI_E_UNKNOWN_ENTRYID;
    }

    if(    ( memcmp(&(id1->instanceGuid), &(id2->instanceGuid), sizeof(MAPIUID)) == 0)
        && ( memcmp(&(id1->typeGuid), &(id2->typeGuid), sizeof(MAPIUID)) == 0) )
    {
        *lpulResult = TRUE;
        return result;
    }

    if(    ( memcmp(&(id1->instanceGuid), &(id2->instanceGuid), sizeof(MAPIUID)) == 0)
        && ( memcmp(&(id1->typeGuid), &(id2->typeGuid), sizeof(MAPIUID)) != 0) )
    {
        //This is very strange
        OTLKCON_DEBUGBREAK;
    }

    return result;
}

STDMETHODIMP otlkcon_generic_msgstore::OpenEntry(
  ULONG cbEntryID,
  LPENTRYID lpEntryID,
  LPCIID lpInterface,
  ULONG ulFlags,
  ULONG *lpulObjType,
  LPUNKNOWN *lppUnk 
)
{
    HRESULT result = S_OK;
    O_ENTRYID *eid;
    ULONG cmpRes = 0;
    HRESULT res = 0;
    O_IFolder *fld;
    O_IMessage *msg;
    O_IProp *attach;

    //Validate_IMsgStore_OpenEntry(this,cbEntryID,lpEntryID,lpInterface,
     //                       ulFlags,lpulObjType,lppUnk);

	OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::OpenEntry");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OpenEntry");

    lastCalledFunctionID = OTLKCON_IMSGSTORE_OPENENTRY;
    strcpy(lastCalledFunctionName, OTLKCON_IMSGSTORE_OPENENTRY_STR);
    SetLastError(S_OK, "");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside IMsgStore->OpenEntry.\n");

    eid = (O_ENTRYID *)lpEntryID;

    map< O_ENTRYID *, void *, otlkcon_entryid_less >::iterator objIter;

    if( eid == NULL 
        || (  memcmp( &(eid->instanceGuid), &(entryID->instanceGuid), sizeof(MAPIUID))==0
           && memcmp( &(eid->typeGuid), &otlkcon_imapifolder_guid, sizeof(MAPIUID))==0 ) )
    {
        // We're being asked for the root Folder
        map<string,O_IFolder*>::iterator i = topLevelFolders.find("ROOT_FOLDER");
        if( i == topLevelFolders.end() )
        {
            OTLKCON_DEBUGBREAK;
        }

        fld = i->second;
        //fld->hierarchyTable->Dump(0);

        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"inside IMsgStore->OpenEntry. returning folder %S\n", fld->objName);

        *lppUnk = fld;
        *lpulObjType = MAPI_FOLDER;
        result = S_OK;
        goto openentry_end;
    }
    
    if( cbEntryID == 0 )
    {
        //Why call with valid memory but zero size???
        result = MAPI_E_INVALID_PARAMETER;
        goto openentry_fail;
    }

    // Let's figure out what interface we can return
    if( lpInterface == NULL )
    {
        // Return the default interface
        ;
    }
    else if( memcmp(lpInterface,&IID_IMAPIProp,sizeof(MAPIUID))==0 &&
            ( eid->supportedInterfaces & OTLKCON_IMAPIPROP_CLASS ) )
    {
        // IMAPIProp
        ;
    }
    else if( memcmp(lpInterface,&IID_IMAPIFolder,sizeof(MAPIUID))==0 &&
            ( eid->supportedInterfaces & OTLKCON_IMAPIFOLDER_CLASS ) )
    {
        // IMAPIFolder
        ;
    }
    else if( memcmp(lpInterface,&IID_IMessage,sizeof(MAPIUID))==0 &&
            ( eid->supportedInterfaces & OTLKCON_IMESSAGE_CLASS ) )
    {
        // IMessage
        ;
    }
    else if( memcmp(lpInterface,&IID_IAttachment,sizeof(MAPIUID))==0 &&
            ( eid->supportedInterfaces & OTLKCON_IATTACHMENT_CLASS ) )
    {
        // IAttachment
        ;
    }
    else
    {
        //  What are they asking for?
        //  Not supported on this object?
		SetLastError(E_NOINTERFACE, "Interface is not supported");
        //OTLKCON_DEBUGBREAK;
		result = E_NOINTERFACE;
        goto openentry_end;
    }

#ifdef OTLKCON_DEBUG
    result = ValidateEntryID( (O_ENTRYID *)lpEntryID, cbEntryID,0,0 );
    if( result != S_OK )
    {
//        OTLKCON_DEBUGBREAK
        ;
    }
#endif

    // See if the object is in memory
    objIter = loadedObjects.find( (O_ENTRYID *)lpEntryID );

    // Forget what they ask for, open the object most accurate interface
    if( memcmp(&(eid->typeGuid),&otlkcon_imapifolder_guid,sizeof(MAPIUID))==0 )
    {
        if( objIter != loadedObjects.end() )
        {
            fld = (O_IFolder *)objIter->second;
//			fld->AddRef();
        }
        else
        {
            fld = new O_IFolder();
            result = fld->Load( this, eid );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK
                goto openentry_fail;
            }
        }

        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"inside IMsgStore->OpenEntry. returning folder %S\n", fld->objName);

        *lppUnk = fld;
        *lpulObjType = MAPI_FOLDER;
    }
    else if( memcmp(&(eid->typeGuid),&otlkcon_imessage_guid,sizeof(MAPIUID))==0 )
    {
        //Henry 11.12.2005
		//For message we need to return new object every time
		//that is because message is transacted object 
		//so it contain only snapshot
		//message sought
        if( objIter != loadedObjects.end() )
        {
            msg = (O_IMessage *)objIter->second;
//			msg->AddRef();
        }
        else
        {
            msg = new O_IMessage();
            result = msg->Load( this, eid );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK
                goto openentry_end;
            }
        }

        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"inside IMsgStore->OpenEntry. returning message '%S'\n", msg->objName);

        *lppUnk = msg;
        *lpulObjType = MAPI_MESSAGE;
    }
    else if( memcmp(&(eid->typeGuid),&otlkcon_iattachment_guid,sizeof(MAPIUID))==0 )
    {
        if( objIter != loadedObjects.end() )
        {
            attach = (O_IProp *)objIter->second;
//			msg->AddRef();
        }
        else
        {
            attach = new O_IProp();
            result = attach->Load( this, eid );
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK
                goto openentry_end;
            }
        }

        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"inside IMsgStore->OpenEntry. returning message '%S'\n", attach->objName);

        *lppUnk = attach;
        *lpulObjType = MAPI_ATTACH;
    }
    else
    {
        //unknown interface
        OTLKCON_DEBUGBREAK;
    }

openentry_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OpenEntry");
	OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::OpenEntry");
    return result;

openentry_fail:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OpenEntry");
	OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::OpenEntry");
    return result;
}

STDMETHODIMP otlkcon_generic_msgstore::SetReceiveFolder(
  LPTSTR lpszMessageClass,
  ULONG ulFlags,
  ULONG cbEntryID,
  LPENTRYID lpEntryID        
)
{
    HRESULT result = MAPI_E_NO_SUPPORT;

    Validate_IMsgStore_SetReceiveFolder(this,lpszMessageClass,ulFlags,
                    cbEntryID,lpEntryID);

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_ERROR, OTLKCON_LOG_TARGET_ALL, 
        L"ERROR: inside IMsgStore->SetReceiveFolder.\n");

    return result;
}

STDMETHODIMP otlkcon_generic_msgstore::GetReceiveFolder(
  LPTSTR lpszMessageClass,
  ULONG ulFlags,
  ULONG *lpcbEntryID,
  LPENTRYID *lppEntryID,
  LPTSTR *lppszExplicitClass  
)
{
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_ERROR, OTLKCON_LOG_TARGET_ALL, 
        L"ERROR: otlkcon_generic_msgstore::GetReceiveFolder() called.\n");

    return MAPI_E_NO_SUPPORT;
}

STDMETHODIMP otlkcon_generic_msgstore::GetReceiveFolderTable(
  ULONG ulFlags,
  LPMAPITABLE *lppTable  
)
{
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_ERROR, OTLKCON_LOG_TARGET_ALL, 
        L"ERROR: otlkcon_generic_msgstore::GetReceiveFolderTable() called.\n");

    return MAPI_E_NO_SUPPORT;
}

STDMETHODIMP otlkcon_generic_msgstore::StoreLogoff(
  ULONG *lpulFlags  
)
{
  HRESULT result = S_OK;

  Validate_IMsgStore_StoreLogoff(this, lpulFlags);

  OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
      L"inside IMsgStore->StoreLogoff.\n");

  m_ulCachedStoreLogOffFlags = *lpulFlags;
  *lpulFlags = LOGOFF_COMPLETE;
  
  return result;
}


STDMETHODIMP otlkcon_generic_msgstore::AbortSubmit(
  ULONG cbEntryID,
  LPENTRYID lpEntryID,
  ULONG ulFlags          
)
{
  HRESULT result = MAPI_E_NO_SUPPORT;

  Validate_IMsgStore_AbortSubmit(this,cbEntryID,lpEntryID,ulFlags);
  OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
      L"inside IMsgStore->AbortSubmit.\n");

  return result;
}

STDMETHODIMP otlkcon_generic_msgstore::GetOutgoingQueue(
  ULONG ulFlags,
  LPMAPITABLE *lppTable  
)
{
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_ERROR, OTLKCON_LOG_TARGET_ALL, 
        L"ERROR: otlkcon_generic_msgstore::GetOutgoingQueue() called.\n");

    return MAPI_E_NO_SUPPORT;
}

STDMETHODIMP otlkcon_generic_msgstore::SetLockState(
  LPMESSAGE lpMessage,
  ULONG ulLockState      
)
{
  HRESULT result = MAPI_E_NO_SUPPORT;

  Validate_IMsgStore_SetLockState(this,lpMessage,ulLockState);

  OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
      L"inside IMsgStore->SetLockState.\n");

  return result;
}

STDMETHODIMP otlkcon_generic_msgstore::FinishedMsg(
  ULONG ulFlags,
  ULONG cbEntryID,
  LPENTRYID lpEntryID  
)
{
  HRESULT result = MAPI_E_NO_SUPPORT;

  Validate_IMsgStore_FinishedMsg(this,ulFlags,cbEntryID,lpEntryID);

  OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
      L"inside IMsgStore->FinishedMsg.\n");
  return result;
}

STDMETHODIMP otlkcon_generic_msgstore::NotifyNewMail(
  LPNOTIFICATION lpNotification  
)
{
  HRESULT result = MAPI_E_NO_SUPPORT;

  Validate_IMsgStore_NotifyNewMail(this,lpNotification);

  OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
      L"inside IMsgStore->NotifyNewMail.\n");

  return result;
}

ULONG otlkcon_generic_msgstore::GetObjCount(void)
{
    return ++objCount;
}

HRESULT otlkcon_generic_msgstore::CreateStoreEntryID(O_ENTRYID** lppNewEntry, otlkcon_memory *mem )
{
	HRESULT hResult = E_INVALIDARG;
	if(NULL != lppNewEntry && NULL != mem)
	{
		hResult = MAPI_E_NOT_ENOUGH_MEMORY;
		*lppNewEntry = (O_ENTRYID*)mem->malloc(sizeof(O_ENTRYID));
		if(NULL != *lppNewEntry)
		{
			MAPIUID RecordKey = {0};
			O_IProp::NewMAPIUID(&RecordKey);
			hResult = O_IProp::CreateEntryID(*lppNewEntry, &RecordKey, RecordKey, otlkcon_imsgstore_guid);
			if(SUCCEEDED(hResult))
			{
				(*lppNewEntry)->typeID = MAPI_STORE;
                (*lppNewEntry)->primarySupportedInterface = OTLKCON_IMSGSTORE_CLASS;
				(*lppNewEntry)->supportedInterfaces |= OTLKCON_IMSGSTORE_CLASS;
			} // if
			else
			{
				mem->free(*lppNewEntry);
				*lppNewEntry = NULL;
			} // else
		} // if
	} // if

	return hResult;
}

HRESULT otlkcon_generic_msgstore::ValidateEntryID( O_ENTRYID *eid, ULONG cbEntryID, ULONG flags, ULONG interfaces )
{
    HRESULT result = S_OK;

    if( eid == NULL )
    {
        return E_FAIL;
    }

    // Sanity check. Is the EntryID ours?
    if( memcmp(&(((O_ENTRYID *)eid)->storeInstanceGuid),&(entryID->instanceGuid),sizeof(MAPIUID)) )
    {
        // Find the store then.
        return E_FAIL;
    }

    if( eid->typeID == 0 || eid->primarySupportedInterface == 0 )
    {
        return E_FAIL;
    }

    if( flags == 0 || flags & OTLKCON_ENTRYID_VALIDATE_SIZE )
    {
        // Sanity check. Correct size?
        if( cbEntryID != sizeof(O_ENTRYID) )
        {
            return E_FAIL;
        }
    }

    if( flags == 0 || flags & OTLKCON_ENTRYID_VALIDATE_EXISTS )
    {
        HRESULT res;
        result = ObjectExists(eid, &res);
        if( res==FALSE )
        {
            return E_FAIL;
        }
    }

    if( flags == 0 || flags & OTLKCON_ENTRYID_VALIDATE_INTERFACES )
    {
        if( interfaces && !(eid->supportedInterfaces & interfaces) )
        {
            return E_FAIL;
        }
    }

    return result;
}

HRESULT otlkcon_generic_msgstore::RemoveObject( O_ENTRYID *eid, O_ENTRYID *parEid )
{
    HRESULT result = S_OK;
    char *tblStr;
    int sqlRes;
    map< O_ENTRYID *, void *, otlkcon_entryid_less >::iterator objIter;

    // We will be modifying the event list
   // OTLKCON_ENTERCRITICALSECTION(&otlkcon_objects_cs, 
     //   "EVENT> otlkcon_generic_msgstore::RemoveObject" );

	OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::RemoveObject");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::RemoveObject");

    otlkcon_sqlite_helper *sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;	
        goto removeobject_end;
	}

    // Notify all interested listeners
    LPNOTIFICATION notif = (LPNOTIFICATION)memObj->malloc( sizeof(NOTIFICATION) );
    notif->ulEventType = fnevObjectDeleted;
    notif->info.obj.cbEntryID = sizeof(O_ENTRYID);
    notif->info.obj.lpEntryID = (LPENTRYID)eid;
    notif->info.obj.ulObjType = eid->typeID;
	ULONG ulNotifyFlags = 0;

    if( parEid )
    {
        notif->info.obj.cbParentID = sizeof(O_ENTRYID);
        notif->info.obj.lpParentID = (LPENTRYID)parEid;
        result = Notify(parEid, 1, notif, &ulNotifyFlags);
    }
    else
    {
        result = Notify( entryID, 1, notif, &ulNotifyFlags );
    }

    // Remove from loaded objects
    objIter = loadedObjects.find( entryID );
    if( objIter != loadedObjects.end() )
    {
        loadedObjects.erase( objIter );
    }

    // Remove from event list
    objIter = otlkcon_event_objects.find( entryID );
    if( objIter != otlkcon_event_objects.end() )
    {
        otlkcon_event_objects.erase( objIter );
    }

    // FIXME: Note that we notify *before* we delete the actual object.
    //        This is because notification handlers may actually want to
    //        interact with the object before it is discarded.
    tblStr = (char *)memObj->malloc(100);
    result = GetTableName(eid->instanceGuid, eid->typeGuid, &tblStr);

    // FIXME:  Check for objects in memory.  Either notify them or
    //         fail.  Remember, there maybe other instances of MsgStore

    // TODO:  Maybe objects should listen for notifications on themselves.
    //        Do they already?
    sqlRes = sqlObj->sqlCommand( "DROP TABLE %s", tblStr );
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;	
        goto removeobject_end;
    }

removeobject_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::RemoveObject");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::RemoveObject");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_objects_cs, 
                                            "EVENT> otlkcon_generic_msgstore::RemoveObject" );

    return result;
}

HRESULT otlkcon_generic_msgstore::ProcessEvent( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;

    switch( ev->eventType )
    {
        case OTLKCON_EVENT_RELOAD_CONFIG:
            result = OnReloadConfig( ev );
            break;

        default:
            OTLKCON_DEBUGBREAK;
            break;
    }

    return result;
}

HRESULT otlkcon_generic_msgstore::OnReloadConfig( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;

    return result;
}
